#include <libgen.h>

#include <string>
using std::string;

#include <iostream>
using std::cerr; using std::cout; using std::endl;

typedef size_t Player;

enum Result
{
    DRAW,
    PLAYER_1,
    PLAYER_2
};

enum CmdLineArguments
{
    ARG_THIS = 0,
    ARG_REFEREE = 1,
    ARG_TIMES = 2,
    FIXED_ARGS
};

class Score
{
    struct Data
    {
        size_t p1_wins;
        size_t p2_wins;
        size_t draws;
        
        Data() : p1_wins(0), p2_wins(0), draws(0) {}
    };
    
    struct Summary
    {
        Player p;
        int score;
    };
    
    const size_t players;
    Data** matrix;
    
    static const char* basename(const char* fullname);
    
public:
    Score(size_t players)
    : players(players)
    {
        matrix = new Data* [players];
        for (size_t i=0; i<players; ++i)
            matrix[i] = new Data[players];
    }
    
    ~Score()
    {
        for (size_t i=0; i<players; ++i)
            delete [] matrix[i];
            
        delete [] matrix;
    }
    
    void match(Player p1, Player p2, Result result)
    {
        switch(result)
        {
            case DRAW:      draw(p1, p2); break;
            case PLAYER_1:  p1_won(p1, p2); break;
            case PLAYER_2:  p2_won(p1, p2); break;
        }
    }
    
    void show_ranking(char** names) const;
    
private:
    void p1_won(Player p1, Player p2)
    {
        matrix[p1][p2].p1_wins++;
    }
    
    void p2_won(Player p1, Player p2)
    {
        matrix[p1][p2].p2_wins++;
    }
    
    void draw(Player p1, Player p2)
    {
        matrix[p1][p2].draws++;
    }
    
    void show_summary(const Summary summary[], char** names) const;
};

static Result do_match(const char* referee, const char* p1_name, const char* p2_name);
static void show_usage();

int main(int argc, char* argv[])
{
    if (argc >= FIXED_ARGS+2)
    {
        const size_t players = argc - FIXED_ARGS;
        Score scores(players);
        const size_t times = atol(argv[ARG_TIMES]);
    
        for (Player p1=0; p1 < players; p1++)
        {
            cerr << "Playing " << argv[p1+FIXED_ARGS] << "\n";
            for (Player p2=0; p2 < players; p2++)
            {
                if (p1 != p2)
                {
                    for (size_t time = 0; time < times; time++)
                        scores.match( p1, p2, do_match(argv[ARG_REFEREE], argv[p1+FIXED_ARGS], argv[p2+FIXED_ARGS]) );
                }
            }
        }
        
        scores.show_ranking(argv+FIXED_ARGS);
    }
    else
    {
        cerr << "Invalid number of arguments" << endl;
        show_usage();
    }
    
    return 0;
}


void Score::show_ranking(char** names) const
{
    Summary* summary = new Summary[players];
    
    cout << endl << " ================= Results ============= " << endl;
    cout << endl << "player1 / player2\t\t\tP1\tP2\tDRAWS" << endl;

    for(Player p=0; p<players; p++)
    {
        summary[p].p = p;
        summary[p].score = 0;
    }

    
    for(Player p1=0; p1<players; p1++)
    {   
        for(Player p2=0; p2<players; p2++)
        {
            if (p1 != p2)
            {
                cout << basename(names[p1]) << " / " << basename(names[p2]);
                cout << "\t" << matrix[p1][p2].p1_wins;
                cout << "\t" << matrix[p1][p2].p2_wins;
                cout << "\t" << matrix[p1][p2].draws;
                cout << endl;
                
                summary[p1].score += matrix[p1][p2].p1_wins;
                summary[p1].score -= matrix[p1][p2].p2_wins;
                
                summary[p2].score -= matrix[p1][p2].p1_wins;
                summary[p2].score += matrix[p1][p2].p2_wins;
            }
        }
    }
        
    cout << endl;
    
    show_summary(summary, names);
    
    delete [] summary;
}

const char* Score::basename(const char* full)
{
    char* tmp = strdup(full);
    const char* ret = ::basename(tmp);
    free(tmp);
    return ret;
}

void Score::show_summary(const Summary summary[], char** names) const
{
    size_t* idx = new size_t[players];
    size_t i,j;
    
    for(i=0; i<players; i++)
        idx[i] = i;

    // sort:    
    for(i=0; i<players-1; i++)
    {
        size_t best=i;
        for(j=i+1; j<players; j++)
        {
            if (summary[idx[j]].score > summary[idx[best]].score)
            {
                const size_t tmp = idx[best];
                idx[best] = idx[j];
                idx[j] = tmp;
            }
        }
    }
    
    for(i=0; i<players; i++)
        cout << names[summary[idx[i]].p] << "\t" << summary[idx[i]].score << endl;    
    
    delete [] idx;
}

static Result do_match(const char* referee, const char* p1_name, const char* p2_name)
{
    string torun;
    
    torun = "./framework";
    torun += " ";
    torun += referee;
    torun += " ";
    torun += p1_name;
    torun += " ";
    torun += p2_name;
    torun += " 1> /dev/null";

    //cerr << torun.c_str() << endl;
        
    int ret = system(torun.c_str());
    
    if (WEXITSTATUS(ret) != 0)
    {
        switch(WEXITSTATUS(ret))
        {
            case DRAW:
            case PLAYER_1:
            case PLAYER_2:
                return Result(WEXITSTATUS(ret));
    
            default:    cerr << "ret=" << ret << endl; return DRAW;
        }
    }
    else
    {
        cerr << "No WEXITSTATUS; ret=" << ret << endl;
        return DRAW;
    }
}

static void show_usage()
{
    cerr<< "Usage:\n\ttournament referee #times player1 player2 [...playern]" << endl;
}
